﻿namespace Summoner
{
    using System.IO;
    using UnityEditor;
    using UnityEngine;
    using System.Collections.Generic;
    using System.Reflection;
#if !UNITY_5_3
    using UnityEngine.Profiling;
    using System.Linq;
#endif

    [InitializeOnLoad]
    public class AssetEditUntil
    {
        public static List<string> FilterExtensions = new List<string>()
        {
            ".cs",
            ".unity",
            ".dll",
            ".mat",
            ".pdf",
            ".rtf",
            ".doc",
            ".fbx", 
            //".png", 
            //".tga", 
            ".anim",
            ".controller",
            ".exr",
            "LightmapSnapshot.asset",
            ".meta"
        };

        public AssetEditUntil()
        {
            AssetDatabase.ImportAsset(AssetPath.AssetVesselPath, ImportAssetOptions.Default);
        }

        /// <summary>
        /// create asset vessel asset
        /// </summary>
        [MenuItem("Apple/Create Asset Vessel", false, 301)]
        private static void CreateAssetCollection()
        {
            Object asset = ScriptableObject.CreateInstance<AssetVessel>();
            if(!Directory.Exists(AssetPath.ConfigPath))
            {
                Directory.CreateDirectory(AssetPath.ConfigPath);
            }
            AssetDatabase.CreateAsset(asset, AssetPath.AssetVesselPath);
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh(ImportAssetOptions.Default);
        }

        [MenuItem("Apple/Refresh Asset Vessel", false, 302)]
        private static void UpdateList()
        {
            AssetVessel vessel = AssetDatabase.LoadAssetAtPath(AssetPath.AssetVesselPath, typeof(AssetVessel)) as AssetVessel;
            for (int i = 0; i < vessel.assetList.Count; i++)
            {
                if (vessel.assetList[i].asset == null)
                {
                    vessel.assetList[i].asset = AssetDatabase.LoadAssetAtPath(vessel.assetList[i].pathInEditor, System.Type.GetType(vessel.assetList[i].type));
                    if (vessel.assetList[i].asset == null)
                    {
                        vessel.assetList.RemoveAt(i);
                    }
                }
                else
                {
                    vessel.assetList[i].pathInEditor = AssetDatabase.GetAssetPath(vessel.assetList[i].asset);
                    vessel.assetList[i].Md5 = MD5Hashing.GetFileHash(vessel.assetList[i].pathInEditor);
                }
            }
            EditorUtility.SetDirty(vessel);
            AssetDatabase.SaveAssets();
        }

        /// <summary>
        /// add asset which in folder to exist asset vessel
        /// </summary>
        [MenuItem("Assets/Add Folder Assets", false, 401)]
        private static void AddFolderAssets()
        {
            if (AssetDatabase.LoadAssetAtPath(AssetPath.AssetVesselPath, typeof(AssetVessel)) == null)
            {
                CreateAssetCollection();
            }
            UpdateList();
            string path = GetSelectedPathOrFallback();
            AssetDatabase.ImportAsset(AssetPath.AssetVesselPath);
            AssetVessel vessel = AssetDatabase.LoadAssetAtPath(AssetPath.AssetVesselPath, typeof(AssetVessel)) as AssetVessel;
            Dictionary<string, AssetInfo> path2info = CreatePathDictionary(vessel.assetList);
            foreach (AssetInfo info in DoScanAssetPath(path))
            {
                if (!path2info.ContainsKey(info.pathInEditor.Replace("\\", "/")))
                {
                    vessel.assetList.Add(info);
                }
                else
                {
                    if(path2info[info.pathInEditor.Replace("\\", "/")].asset == null)
                    {
                        vessel.assetList.Remove(path2info[info.pathInEditor.Replace("\\", "/")]);
                        vessel.assetList.Add(info);
                    }
                }
            }
            vessel.assetList.Sort((a1, a2) => { return a1.Name.CompareTo(a2.Name); });
            CheckRepeat();
        }

        [MenuItem("Assets/Remove Folder Assets", false, 402)]
        private static void RemoveFolderAssets()
        {
            if (AssetDatabase.LoadAssetAtPath(AssetPath.AssetVesselPath, typeof(AssetVessel)) != null)
            {
                UpdateList();
                string path = GetSelectedPathOrFallback();
                AssetDatabase.ImportAsset(AssetPath.AssetVesselPath);
                AssetVessel vessel = AssetDatabase.LoadAssetAtPath(AssetPath.AssetVesselPath, typeof(AssetVessel)) as AssetVessel;
                Dictionary<string, AssetInfo> path2info = CreatePathDictionary(vessel.assetList);
                foreach(AssetInfo info in DoScanAssetPath(path))
                {
                    if (path2info.ContainsKey(info.pathInEditor.Replace("\\", "/")))
                    {
                        vessel.assetList.Remove(path2info[info.pathInEditor.Replace("\\", "/")]);
                    }
                }
                CheckRepeat();
            }
        }

        [MenuItem("Assets/Generate Sprite Config", false, 305)]
        static void GenerateSpriteConfig()
        {
            if (Selection.activeObject == null) { return; }
            string spriteSheet = AssetDatabase.GetAssetPath(Selection.activeObject);
            Sprite[] sprites = AssetDatabase.LoadAllAssetsAtPath(spriteSheet).OfType<Sprite>().ToArray();
            List<SpriteInfo> spriteList = new List<SpriteInfo>();
            for (int i = 0; i < sprites.Length; i++)
            {
                spriteList.Add(new SpriteInfo() { name = sprites[i].name, rect = sprites[i].rect, pivot = sprites[i].pivot, border = sprites[i].border, pixelsPerUnit = sprites[i].pixelsPerUnit });
            }

            JsonSerializer.PackJsonToFile(GetSelectedPathOrFallback(), spriteList, Selection.activeObject.name + "_cfg");
            EditorUtility.ClearProgressBar();
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();
        }

        public static void CheckRepeat()
        {
            AssetVessel vessel = AssetDatabase.LoadAssetAtPath(AssetPath.AssetVesselPath, typeof(AssetVessel)) as AssetVessel;
            vessel.repeatList.Clear();
            if (vessel.assetList.Count > 0)
            {
                for (int i = 0; i < vessel.assetList.Count; i++)
                {
                    RepeatAsset repeatcan = new RepeatAsset();
                    for (int j = i + 1; j < vessel.assetList.Count; j++)
                    {
                        if (i != j)
                        {
                            if (vessel.assetList[i] != null && vessel.assetList[j] != null)
                            {
                                if (vessel.assetList[i].assetName == vessel.assetList[j].assetName)
                                {
                                    repeatcan.repeats.Add(vessel.assetList[j]);
                                }
                            }
                        }
                    }
                    if (repeatcan.repeats.Count > 0)
                    {
                        repeatcan.assetName = vessel.assetList[i].assetName;
                        repeatcan.repeats.Add(vessel.assetList[i]);
                        if (!CheckExisit(vessel, repeatcan))
                        {
                            vessel.repeatList.Add(repeatcan);
                        }
                    }
                }
            }
            EditorUtility.SetDirty(vessel);
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();
        }

        private static bool CheckExisit(AssetVessel vessel, RepeatAsset repeatcan)
        {
            if (vessel.repeatList.Count == 0)
            {
                return false;
            }
            Dictionary<string, RepeatAsset> name2repeat = new Dictionary<string, RepeatAsset>();
            foreach (RepeatAsset repeat in vessel.repeatList)
            {
                if (name2repeat.ContainsKey(repeat.assetName))
                {
                    name2repeat[repeat.assetName] = repeat;
                }
            }
            if (name2repeat.ContainsKey(repeatcan.assetName))
            {
                return true;
            }
            return false;
        }

        public static void CreatrAssetConfiguration(AssetVessel vessel)
        {
            GameConfigInfo information = new GameConfigInfo() { version = PlayerSettings.bundleVersion, platform = Application.platform, language = SystemLanguage.Chinese, assets = vessel.assetList };
            JsonSerializer.PackJsonToFile(AssetPath.ConfigPath, information, AssetPath.AssetConfigName.ToLower());
        }

        private static Dictionary<string, AssetInfo> CreatePathDictionary(List<AssetInfo> infoList)
        {
            Dictionary<string, AssetInfo> path2info = new Dictionary<string, AssetInfo>();
            foreach (AssetInfo info in infoList)
            {
                path2info[info.pathInEditor.Replace("\\", "/")] = info;
            }
            return path2info;
        }

        private static List<AssetInfo> DoScanAssetPath(string folder)
        {
            List<AssetInfo> assetList = new List<AssetInfo>();
            string[] files = Directory.GetFiles(folder, "*.*", System.IO.SearchOption.AllDirectories);
            for (int i = 0; i < files.Length; i++)
            {
                if (DoNotFilter(files[i]))
                {
                    string path = FilterPath(files[i]);
                    Object obj = AssetDatabase.LoadAssetAtPath(path, typeof(Object));
                    if (obj != null)
                    {
                        assetList.Add(new AssetInfo() { asset = obj, assetName = obj.name, type = obj.GetType().ToString(), pathInEditor = path, Name = FilterName(obj.name), FileSize = (obj.GetType().Equals(typeof(UnityEngine.Texture2D))) ? GetFileSizeOnDisk(obj) : (int)(new FileInfo(path).Length), RamSize = Profiler.GetRuntimeMemorySize(obj), Md5 = MD5Hashing.GetFileHash(files[i]) });
                    }
                }
            }
            return assetList;
        }

        /// <summary>
        /// get unity local path
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static string FilterPath(string str)
        {
            int index = str.IndexOf("Assets");
            if (index > 0)
            {
                return str.Substring(index);
            }
            return str;
        }

        private static string FilterName(string str)
        {
            int index = str.IndexOf("-");
            if(index > 0)
            {
                return str.Substring(0, index);
            }
            return str;
        }

        private static bool DoNotFilter(string path)
        {
            foreach (string str in FilterExtensions)
            {
                if (path.ToLower().EndsWith(str.ToLower()))
                {
                    return false;
                }
            }
            return true;
        }

        /// <summary>
        /// get the selected path
        /// </summary>
        /// <returns></returns>
        public static string GetSelectedPathOrFallback()
        {
            string path = "Assets";
            foreach (Object obj in Selection.GetFiltered(typeof(Object), SelectionMode.Assets))
            {
                path = AssetDatabase.GetAssetPath(obj);
                if (!string.IsNullOrEmpty(path) && File.Exists(path))
                {
                    path = Path.GetDirectoryName(path);
                    break;
                }
            }
            return path;
        }

        public static int GetFileSizeOnDisk(Object obj)
        {
#if UNITY_5_5 || UNITY_5_6
            System.Type type = System.Reflection.Assembly.Load("UnityEditor.dll").GetType("UnityEditor.TextureUtil");
#else
            var type = Types.GetType("UnityEditor.TextureUtil", "UnityEditor.dll");
#endif
            MethodInfo methodInfo = type.GetMethod("GetStorageMemorySize", BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public);
            return (int)methodInfo.Invoke(null, new object[] { obj });
        }
    }
}